package com.raorao.rpc.client;

import com.raorao.rpc.client.discovery.ServiceDiscoverer;
import com.raorao.rpc.client.net.NetClient;
import com.raorao.rpc.common.constants.LoadBalanceEnum;
import com.raorao.rpc.common.constants.ServiceStatusEnum;
import com.raorao.rpc.common.protocol.RpcRequest;
import com.raorao.rpc.common.protocol.RpcResponse;
import com.raorao.rpc.common.protocol.MessageProtocol;
import com.raorao.rpc.common.service.ServiceInfo;
import com.raorao.rpc.common.utils.LoadBalanceUtils;
import com.raorao.rpc.exception.RpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import static java.lang.reflect.Proxy.newProxyInstance;

/**
 * 客户端代理工厂：用于创建远程服务代理类
 * 封装编组请求、请求发送、编组响应等操作。
 *
 * @author raorao
 * @since 1.0.0
 */
public class ClientProxyFactory {

    private static Logger logger = LoggerFactory.getLogger(ClientProxyFactory.class);

    /**
     * 服务发现者
     */
    private ServiceDiscoverer serviceDiscoverer;

    /**
     * 协议清单
     */
    private Map<String, MessageProtocol> supportMessageProtocols;

    /**
     * 设置网络层实现
     */
    private NetClient netClient;

    private Map<Class<?>, Object> objectCache = new HashMap<>();

    @Autowired
    private LoadBalanceUtils loadBalanceUtils;

    /**
     * 通过Java动态代理获取服务代理类
     *
     * @param clazz 被代理类Class
     * @param <T>   泛型
     * @return 服务代理类
     */
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) this.objectCache.computeIfAbsent(clazz,
                cls -> newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new ClientInvocationHandler(cls)));
    }

    /**
     * 客户端服务代理类invoke函数细节实现
     */
    private class ClientInvocationHandler implements InvocationHandler {
        private Class<?> clazz;

        public ClientInvocationHandler(Class<?> clazz) {
            super();
            this.clazz = clazz;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {

            if (method.getName().equals("toString")) {
                return proxy.getClass().toString();
            }

            if (method.getName().equals("hashCode")) {
                return 0;
            }

            // 1、获得服务信息
            String serviceName = this.clazz.getName();
            List<ServiceInfo> services = serviceDiscoverer.getServices(serviceName);

            if (services == null || services.isEmpty()) {
                logger.error("【rpc服务调用】No provider available!");
                throw new RpcException("【rpc服务调用】No provider available!");
            }

            // TODO 服务握手机制、用于检测服务健康状态
            // 随机选择一个服务提供者（软负载均衡）
            ServiceInfo service = loadBalanceUtils.getServiceInfo(services);

            // 2、构造request对象
            RpcRequest req = new RpcRequest();
            req.setServiceName(service.getName());
            req.setMethod(method.getName());
            req.setParameterTypes(method.getParameterTypes());
            req.setParameters(args);

            // 3、协议层编组
            // 获得该方法对应的协议
            MessageProtocol protocol = supportMessageProtocols.get(service.getProtocol());
            // 编组请求
            byte[] data = protocol.marshallingRequest(req);

            // TODO 缺少访问失败重试机制
            // 4、调用网络层发送请求
            byte[] repData = netClient.sendRequest(data, service);

            // 5解组响应消息
            RpcResponse rsp = protocol.unmarshallingResponse(repData);

            // 6、结果处理
            if (rsp.getException() != null) {
                logger.error("【rpc 服务调用失败】: 服务地址={}", service.getAddress());
                service.setStatus(ServiceStatusEnum.NETWORKERRO.getValue());
                logger.error("【rpc 服务提供者调用重试】");
                this.invoke(proxy, method, args);
                throw rsp.getException();
            }
            return rsp.getReturnValue();
        }
    }

    public ServiceDiscoverer getServiceDiscoverer() {
        return serviceDiscoverer;
    }

    public void setServiceDiscoverer(ServiceDiscoverer serviceDiscoverer) {
        this.serviceDiscoverer = serviceDiscoverer;
    }

    public Map<String, MessageProtocol> getSupportMessageProtocols() {
        return supportMessageProtocols;
    }

    public void setSupportMessageProtocols(Map<String, MessageProtocol> supportMessageProtocols) {
        this.supportMessageProtocols = supportMessageProtocols;
    }

    public NetClient getNetClient() {
        return netClient;
    }

    public void setNetClient(NetClient netClient) {
        this.netClient = netClient;
    }


}
